/***************************************************************
 * Name:      gameMain.cpp
 * Purpose:   Code for Application Frame
 * Author:    Bruno, Henrique, Hugo ()
 * Created:   2012-06-01
 * Copyright:
 * License:
 **************************************************************/

#include <wx/msgdlg.h>

//(*InternalHeaders(gameFrame)
#include <wx/string.h>
#include <wx/intl.h>
//*)
#include "gameMain.h"

//helper functions
enum wxbuildinfoformat {
    short_f, long_f };

wxString wxbuildinfo(wxbuildinfoformat format)
{
    wxString wxbuild(wxVERSION_STRING);

    if (format == long_f )
    {
#if defined(__WXMSW__)
        wxbuild << _T("-Windows");
#elif defined(__UNIX__)
        wxbuild << _T("-Linux");
#endif

#if wxUSE_UNICODE
        wxbuild << _T("-Unicode build");
#else
        wxbuild << _T("-ANSI build");
#endif // wxUSE_UNICODE
    }

    return wxbuild;
}

//(*IdInit(gameFrame)
const long gameFrame::idMenuQuit = wxNewId();
const long gameFrame::idMenuAbout = wxNewId();
const long gameFrame::ID_STATUSBAR1 = wxNewId();
//*)
const long gameFrame::idMenuNewGame = wxNewId();
const long gameFrame::idMenuSlow = wxNewId();
const long gameFrame::idMenuNormal = wxNewId();
const long gameFrame::idMenuFast = wxNewId();
const long gameFrame::idMenuLinear = wxNewId();
const long gameFrame::idMenuAccelerated = wxNewId();
const long gameFrame::idMenuFixed = wxNewId();
const long gameFrame::idMenuRandom = wxNewId();

BEGIN_EVENT_TABLE(gameFrame,wxFrame)
    //(*EventTable(gameFrame)
    //*)
END_EVENT_TABLE()

gameFrame::gameFrame(wxWindow* parent,wxWindowID id)
{
    //(*Initialize(gameFrame)
    wxMenuItem* MenuItem8;
    wxMenuItem* MenuItem7;
    wxMenuItem* MenuItem5;
    wxMenuItem* MenuItem2;
    wxMenu* Menu3;
    wxMenuItem* MenuItem1;
    wxMenuItem* MenuItem4;
    wxMenu* Menu1;
    wxMenuItem* MenuItem10;
    wxMenuItem* MenuItem6;
    wxMenuBar* MenuBar1;
    wxMenu* Menu2;
    wxMenuItem* MenuItem9;
    wxMenu* Menu4;

    Create(parent, id, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxDEFAULT_FRAME_STYLE, _T("id"));
    SetClientSize(wxSize(200,500));
        MenuBar1 = new wxMenuBar();
    Menu1 = new wxMenu();
    MenuItem3 = new wxMenuItem(Menu1, idMenuNewGame, _("New Game\tCtrl+n"), _("Creates a new game"), wxITEM_NORMAL);
    Menu1->Append(MenuItem3);
    MenuItem1 = new wxMenuItem(Menu1, idMenuQuit, _("Quit\tAlt-F4"), _("Quit the application"), wxITEM_NORMAL);
    Menu1->Append(MenuItem1);
    MenuBar1->Append(Menu1, _("&Game"));
    Menu3 = new wxMenu();
    MenuItem4 = new wxMenuItem(Menu3, idMenuSlow, _("Slow"), wxEmptyString, wxITEM_NORMAL);
    Menu3->Append(MenuItem4);
    MenuItem5 = new wxMenuItem(Menu3, idMenuNormal, _("Normal"), wxEmptyString, wxITEM_NORMAL);
    Menu3->Append(MenuItem5);
    MenuItem6 = new wxMenuItem(Menu3, idMenuFast, _("Fast"), wxEmptyString, wxITEM_NORMAL);
    Menu3->Append(MenuItem6);
    MenuItem7 = new wxMenuItem(Menu3, idMenuLinear, _("Linear"), wxEmptyString, wxITEM_NORMAL);
    Menu3->Append(MenuItem7);
    MenuItem8 = new wxMenuItem(Menu3, idMenuAccelerated, _("Accelerated"), wxEmptyString, wxITEM_NORMAL);
    Menu3->Append(MenuItem8);
    MenuBar1->Append(Menu3, _("Speed"));
    Menu4 = new wxMenu();
    MenuItem9 = new wxMenuItem(Menu4, idMenuFixed, _("Fixed"), wxEmptyString, wxITEM_NORMAL);
    Menu4->Append(MenuItem9);
    MenuItem10 = new wxMenuItem(Menu4, idMenuRandom, _("Random"), wxEmptyString, wxITEM_NORMAL);
    Menu4->Append(MenuItem10);
    MenuBar1->Append(Menu4, _("Colour"));
    Menu2 = new wxMenu();
    MenuItem2 = new wxMenuItem(Menu2, idMenuAbout, _("About\tF1"), _("Show info about this application"), wxITEM_NORMAL);
    Menu2->Append(MenuItem2);
    MenuBar1->Append(Menu2, _("Help"));
    SetMenuBar(MenuBar1);
    StatusBar1 = new wxStatusBar(this, ID_STATUSBAR1, 0, _T("ID_STATUSBAR1"));
    int __wxStatusBarWidths_1[1] = { -1 };
    int __wxStatusBarStyles_1[1] = { wxSB_NORMAL };
    StatusBar1->SetFieldsCount(1,__wxStatusBarWidths_1);
    StatusBar1->SetStatusStyles(1,__wxStatusBarStyles_1);
    SetStatusBar(StatusBar1);

    Connect(idMenuNewGame,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&gameFrame::OnNewGame);
    Connect(idMenuQuit,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&gameFrame::OnQuit);
    Connect(idMenuSlow,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&gameFrame::OnSlow);
    Connect(idMenuNormal,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&gameFrame::OnNormal);
    Connect(idMenuFast,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&gameFrame::OnFast);
    Connect(idMenuLinear,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&gameFrame::OnLinear);
    Connect(idMenuAccelerated,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&gameFrame::OnAccelerated);
    Connect(idMenuFixed,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&gameFrame::OnFixed);
    Connect(idMenuRandom,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&gameFrame::OnRandom);
    Connect(idMenuAbout,wxEVT_COMMAND_MENU_SELECTED,(wxObjectEventFunction)&gameFrame::OnAbout);
    //*)

    screen = new Screen(this);
    screen->SetFocus();
    screen->Start();
}

gameFrame::~gameFrame()
{
    //(*Destroy(gameFrame)
    //*)
}

void gameFrame::OnQuit(wxCommandEvent& event)
{
    Close();
}

void gameFrame::OnAbout(wxCommandEvent& event)
{
    wxString msg = wxT("Keys :\n - DOWN/UP : Rotates pieces\n - d : Moves pieces one line down\n - SPACE : Drops pieces down\n - p : Pause game");
    wxMessageBox(msg, _("Game Help"));
}

Screen::Screen(wxFrame *parent)
       : wxPanel(parent, wxID_ANY, wxDefaultPosition,
             wxDefaultSize, wxBORDER_NONE)
{
    timer = new wxTimer(this, 1);

    m_stsbar = parent->GetStatusBar();
    m_notFalling = false;
    m_started = false;
    m_paused = false;
    m_linesRemoved = 0;
    m_curX = 0;
    m_curY = 0;

    m_speed = 300;
    m_accelerated = false;
    m_randomColour = false;

    ClearScreen();

    Connect(wxEVT_PAINT, wxPaintEventHandler(Screen::OnPaint));
    Connect(wxEVT_KEY_DOWN, wxKeyEventHandler(Screen::OnKeyDown));
    Connect(wxEVT_TIMER, wxTimerEventHandler(Screen::OnTimer));

    wxString str;
    str.Printf(wxT("Score : %d"), m_linesRemoved);
    m_stsbar->SetStatusText(str);

    SetBackgroundColour(wxColour(255, 255, 255));
}


void Screen::Start()
{
    if (m_paused)
        return;

    m_started = true;
    m_notFalling = false;
    m_linesRemoved = 0;
    ClearScreen();

    NewPiece();
    timer->Start(m_speed);
}

void Screen::Pause()
{
    if (m_started)
    {
        m_paused = !m_paused;
        if (m_paused) {
            timer->Stop();
            m_stsbar->SetStatusText(wxT("Game paused"));
        } else {
            timer->Start(m_speed);
            wxString str;
            str.Printf(wxT("Score : %d"), m_linesRemoved);
            m_stsbar->SetStatusText(str);
        }
        Refresh();
    }
}

void Screen::OnPaint(wxPaintEvent& event)
{
    wxPaintDC dc(this);

    wxSize size = GetClientSize();
    int screenTop = size.GetHeight() - ScreenHeight * SquareHeight();


    for (int i = 0; i < ScreenHeight; ++i) {
        for (int j = 0; j < ScreenWidth; ++j) {
            Tetrominoes shape = ShapeAt(j, ScreenHeight - i - 1);
            if (shape != NoShape)
                DrawSquare(dc, 0 + j * SquareWidth(),
                           screenTop + i * SquareHeight(), shape);
        }
    }

    if (m_curPiece.GetShape() != NoShape) {
        for (int i = 0; i < 4; ++i) {
            int x = m_curX + m_curPiece.x(i);
            int y = m_curY - m_curPiece.y(i);
            DrawSquare(dc, 0 + x * SquareWidth(),
                       screenTop + (ScreenHeight - y - 1) * SquareHeight(),
                       m_curPiece.GetShape());
        }
    }
}

void Screen::OnKeyDown(wxKeyEvent& event)
{
    if (!m_started || m_curPiece.GetShape() == NoShape) {
        event.Skip();
        return;
    }

    int keycode = event.GetKeyCode();

    if (keycode == 'p' || keycode == 'P') {
	Pause();
        return;
    }

    if (m_paused)
        return;

    switch (keycode) {
    case WXK_LEFT:
        TryMove(m_curPiece, m_curX - 1, m_curY);
        break;
    case WXK_RIGHT:
        TryMove(m_curPiece, m_curX + 1, m_curY);
        break;
    case WXK_DOWN:
        TryMove(m_curPiece.RotateRight(), m_curX, m_curY);
        break;
    case WXK_UP:
        TryMove(m_curPiece.RotateLeft(), m_curX, m_curY);
        break;
    case WXK_SPACE:
        DropDown();
        break;
    case 'd':
        OneLineDown();
        break;
    case 'D':
        OneLineDown();
        break;
    default:
        event.Skip();
    }

}

void Screen::OnTimer(wxTimerEvent& event)
{
    if (m_notFalling) {
        m_notFalling = false;
        NewPiece();
    } else {
        OneLineDown();
    }
}

void Screen::ClearScreen()
{
    for (int i = 0; i < ScreenHeight * ScreenWidth; ++i)
        screen[i] = NoShape;
}

void Screen::DropDown()
{
    int newY = m_curY;
    while (newY > 0) {
        if (!TryMove(m_curPiece, m_curX, newY - 1))
            break;
        newY--;
    }
    PieceDropped();
}

void Screen::OneLineDown()
{
    if (!TryMove(m_curPiece, m_curX, m_curY - 1))
        PieceDropped();
}

void Screen::PieceDropped()
{
    for (int i = 0; i < 4; i++) {
        int x = m_curX + m_curPiece.x(i);
        int y = m_curY - m_curPiece.y(i);
        ShapeAt(x, y) = m_curPiece.GetShape();
    }

    RemoveFullLines();

    timer->Start(m_speed);
    if (m_accelerated){m_speed/=1.05; timer->Start(m_speed);}
    if (!m_notFalling) NewPiece();
}

void Screen::RemoveFullLines()
{
     int numFullLines = 0;

     for (int i = ScreenHeight - 1; i >= 0; i--) {
         bool lineIsFull = true;

         for (int j = 0; j < ScreenWidth; j++) {
             if (ShapeAt(j, i) == NoShape) {
                 lineIsFull = false;
                 break;
             }
         }

         if (lineIsFull) {
             numFullLines++;
             for (int k = i; k < ScreenHeight - 1; k++) {
                 for (int j = 0; j < ScreenWidth; j++)
                     ShapeAt(j, k) = ShapeAt(j, k + 1);
             }
         }
     }

     if (numFullLines > 0) {
         m_linesRemoved += numFullLines;
         wxString str;
         str.Printf(wxT("Score : %d"), m_linesRemoved);
         m_stsbar->SetStatusText(str);

         m_notFalling = true;
         m_curPiece.SetShape(NoShape);
         Refresh();
     }
 }

void Screen::NewPiece()
{
    m_curPiece.SetRandomShape();
    m_curX = ScreenWidth / 2 + 1;
    m_curY = ScreenHeight - 1 + m_curPiece.MinY();

    if (!TryMove(m_curPiece, m_curX, m_curY)) {
        m_curPiece.SetShape(NoShape);
        timer->Stop();
        m_started = false;
        m_stsbar->SetStatusText(wxT("Game Over!!!"));
    }
}

bool Screen::TryMove(const Shape& newPiece, int newX, int newY)
{
    for (int i = 0; i < 4; ++i) {
        int x = newX + newPiece.x(i);
        int y = newY - newPiece.y(i);
        if (x < 0 || x >= ScreenWidth || y < 0 || y >= ScreenHeight)
            return false;
        if (ShapeAt(x, y) != NoShape)
            return false;
    }

    m_curPiece = newPiece;
    m_curX = newX;
    m_curY = newY;
    Refresh();
    return true;
}

void Screen::DrawSquare(wxPaintDC& dc, int x, int y, Tetrominoes shape)
{
    static wxColour colors[] =
            {wxColour(0, 0, 0), wxColour(255, 0, 0),
             wxColour(0, 255, 0), wxColour(0, 0, 255),
             wxColour(255, 255, 0), wxColour(255, 0, 255),
             wxColour(0, 255, 255), wxColour(150, 150, 150) };

    dc.SetPen(*wxTRANSPARENT_PEN);
    if (!m_randomColour) {dc.SetBrush(wxBrush(wxColour(colors[shape])));}
    else {dc.SetBrush(wxBrush(wxColour(rand()%256,rand()%256,rand()%256)));}
    dc.DrawRectangle(x + 1, y + 1, SquareWidth() - 2, SquareHeight() - 2);
}

void gameFrame::OnNewGame(wxCommandEvent& event)
{

    screen->m_started = true;
    screen->m_notFalling = false;
    screen->m_linesRemoved = 0;
    screen->m_speed = 300;
    screen->ClearScreen();
    screen->timer->Start(screen->m_speed);

    wxString str;
    str.Printf(wxT("Score : %d"), screen->m_linesRemoved);
    screen->m_stsbar->SetStatusText(str);
}


void gameFrame::OnSlow(wxCommandEvent& event)
{
    screen->m_speed = 500;
}

void gameFrame::OnNormal(wxCommandEvent& event)
{
    screen->m_speed = 300;
}

void gameFrame::OnFast(wxCommandEvent& event)
{
    screen->m_speed = 100;
}

void gameFrame::OnLinear(wxCommandEvent& event)
{
    screen->m_accelerated = false;
}

void gameFrame::OnAccelerated(wxCommandEvent& event)
{
    screen->m_accelerated = true;
}

void gameFrame::OnFixed(wxCommandEvent& event)
{
    screen->m_randomColour = false;
}

void gameFrame::OnRandom(wxCommandEvent& event)
{
    screen->m_randomColour = true;
}
